#include "messagepack_reader.h"
#include "../exception/jtt809_exception.h"
#include <boost/locale.hpp>
#include <boost/stacktrace/stacktrace.hpp>
#include <iostream>
#include <boost/crc.hpp>
#include <boost/endian/conversion.hpp>

#include "../../jtt809_logger.h"

using namespace protocol;

bool MessagePackReader::tryDecode(char *_buffer, uint8_t &decoded) {
    auto buffer = reinterpret_cast<unsigned char*>(_buffer);
    const uint16_t bytes = (*buffer) << 8 | *(buffer+1);
    if (bytes == decode5a01) {
        decoded = 0x5b;
        return true;
    }
    if (bytes == decode5a02) {
        decoded = 0x5a;
        return true;
    }
    if (bytes == decode5e01) {
        decoded = 0x5d;
        return true;
    }
    if (bytes == decode5e02) {
        decoded = 0x5e;
        return true;
    }
    decoded = bytes >> 8 & 0xff;
    return false;
}

MessagePackReader::MessagePackReader(const char *buffer, const size_t length) : length(length), readerLength(0),
    position(0) {
    srcBuffer = new char[length];
    reader = new char[length];
    memcpy(srcBuffer, buffer, length);
}

MessagePackReader::~MessagePackReader() {
    delete[] reader;
    delete[] srcBuffer;
}

void MessagePackReader::decode() {
    if (isDecoded) return;

    uint8_t byte;
    for (int i = 0; i < length-1; ++i) {
        if (tryDecode(srcBuffer+i, byte))
            i++;
        memcpy(reader+readerLength++, &byte, 1);
    }
    memcpy(reader+readerLength++, srcBuffer+length-1, 1);

    isDecoded = true;
}

bool MessagePackReader::validate() {
    if (!isDecoded) decode();
    boost::crc_ccitt_false_t computer;
    computer.process_bytes(reader+1, readerLength-1-3);
    const auto crc = computer.checksum();
    auto net = boost::endian::native_to_big(crc);
    return net == *reinterpret_cast<uint16_t*>(reader+readerLength-3);
}

void MessagePackReader::readByte(uint8_t &value)
{
    decode();
    range_check(readerLength, position, sizeof(uint8_t));
    value = *(reinterpret_cast<std::uint8_t *>(reader) + position++);
}

void MessagePackReader::readTime(time_t &time) {
    decode();
    range_check(readerLength, position, sizeof(time_t));
    time = boost::endian::big_to_native(*reinterpret_cast<time_t*>(reader+position));
    position += sizeof(time_t);
}

void MessagePackReader::decryptPart(const size_t size, const uint32_t& key, const EncryptOption& option) const {
    const auto decrypted = decrypt(reinterpret_cast<byte*>(reader+position), size, key, option);
    memcpy(reader+position, decrypted, size);
}
